package maze;

import java.awt.Point;
import java.util.LinkedList;
import java.util.Random;

/**
 * A logic maze.
 * @author Team Deer
 * 
 */
public class MazeStructure {

	private final MazeCellStructure[][] mazeCells;
	private static Random random = new Random();

	/**
	 * Constructs a new logic maze.
	 * @param width cells
	 * @param height cells
	 */
	// Constructor
	
	public MazeStructure(final int width, final int height) {
		this.mazeCells = new MazeCellStructure[width][height];

		final LinkedList<Integer> possibleDirections = new LinkedList<Integer>();

		// Add starting point
		// int x = 0;
		// int y = 0;

		final Point pos = new Point(0, 0);
		this.mazeCells[pos.x][pos.y] = new MazeCellStructure();

		// Start of maze
		this.mazeCells[pos.x + 1][pos.y] = new MazeCellStructure();
		this.mazeCells[pos.x][pos.y].walls.remove(Wall.EAST);
		this.mazeCells[pos.x + 1][pos.y].walls.remove(Wall.WEST);
		pos.x++;
		this.mazeCells[pos.x][pos.y].cameFrom = 0;

		while (!(pos.x == 0 && pos.y == 0)) {
			possibleDirections.clear();
			calculatePossibleDirections(pos, possibleDirections);
			// If there is any possible directions
			if (possibleDirections.size() > 0) {
				final int nextDirection = possibleDirections.get(random
						.nextInt(possibleDirections.size()));
				changeMazeCell(pos, nextDirection);

			} else {
				final int dx = (this.mazeCells[pos.x][pos.y].cameFrom - 1) % 2;
				final int dy = -(this.mazeCells[pos.x][pos.y].cameFrom - 2) % 2;
				pos.setLocation(pos.x + dx, pos.y + dy);
			}
		}
		this.mazeCells[width - 1][height - 1].walls.remove(Wall.EAST);
	}

	// Methods
	
	private void calculatePossibleDirections(final Point pos,
			final LinkedList<Integer> possibleDirections) {
		if (pos.x + 1 != this.mazeCells.length
				&& this.mazeCells[pos.x + 1][pos.y] == null) {
			possibleDirections.add(2);
		}
		if (pos.y + 1 != this.mazeCells[0].length
				&& this.mazeCells[pos.x][pos.y + 1] == null) {
			possibleDirections.add(1);
		}
		if (pos.x != 0 && this.mazeCells[pos.x - 1][pos.y] == null) {
			possibleDirections.add(0);
		}
		if (pos.y != 0 && this.mazeCells[pos.x][pos.y - 1] == null) {
			possibleDirections.add(3);
		}
	}

	private void changeMazeCell(final Point pos, final int nextDirection) {
		if (nextDirection == 0) {
			this.mazeCells[pos.x - 1][pos.y] = new MazeCellStructure();
			this.mazeCells[pos.x][pos.y].walls.remove(Wall.WEST);
			this.mazeCells[pos.x - 1][pos.y].walls.remove(Wall.EAST);
			pos.x--;
		} else if (nextDirection == 1) {
			this.mazeCells[pos.x][pos.y + 1] = new MazeCellStructure();
			this.mazeCells[pos.x][pos.y].walls.remove(Wall.SOUTH);
			this.mazeCells[pos.x][pos.y + 1].walls.remove(Wall.NORTH);
			pos.y++;
		} else if (nextDirection == 2) {
			this.mazeCells[pos.x + 1][pos.y] = new MazeCellStructure();
			this.mazeCells[pos.x][pos.y].walls.remove(Wall.EAST);
			this.mazeCells[pos.x + 1][pos.y].walls.remove(Wall.WEST);
			pos.x++;
		} else if (nextDirection == 3) {
			this.mazeCells[pos.x][pos.y - 1] = new MazeCellStructure();
			this.mazeCells[pos.x][pos.y].walls.remove(Wall.NORTH);
			this.mazeCells[pos.x][pos.y - 1].walls.remove(Wall.SOUTH);
			pos.y--;
		}
		this.mazeCells[pos.x][pos.y].cameFrom = (nextDirection + 2) % 4;
	}

	/**
	 * 
	 * @return width of the maze
	 */
	public int getWidth() {
		return this.mazeCells.length;
	}

	/**
	 * 
	 * @return height of the maze
	 */
	public int getHeight() {
		return this.mazeCells[0].length;
	}

	/**
	 * 
	 * @param x
	 * @param y
	 * @param direction
	 *            - the direction to check
	 * @return true if the cell has wall
	 */
	public boolean cellHasWall(final int x, final int y, final Wall direction) {
		return this.mazeCells[x][y].walls.contains(direction);
	
	}

}
